module nade.grid;

import nade.vector;


class Grid(T) {
	abstract Grid!(T) subGrid(const Vector2i from, const Vector2i to);
	abstract void fill(T value);

	abstract T* get(uint x, uint y);
	abstract T opIndex(uint x, uint y, T def = T.init);
	abstract T opIndexAssign(T rhs, uint x, uint y, T def = T.init);
	abstract int opApply(int delegate(ref Vector2i, ref T));

	abstract uint width();
	abstract uint height();
	abstract T[] row(uint i);
}


class Grid(T, uint W, uint H) : Grid!(T) {
	static const WIDTH = W;
	static const HEIGHT = H;

	this()
	{
	}
	this(T def)
	{
		fill(def);
	}

	override Grid!(T) subGrid(const Vector2i from, const Vector2i to)
	{
		auto r = new SubGrid!(T)(from, to);

		for(int y = from.y;  y < to.y;  ++y)
		{
			r._cells ~= _cells[y][from.x .. to.x];
		}

		return r;
	}

	override void fill(T value)
	{
		foreach(ref row; _cells)
			foreach(ref cell; row)
				cell = value;
	}

	override T* get(uint x, uint y)
	{
		if(x >= W  ||  y >= H) return null;
		return _cells[y].ptr + x;
	}
	override T opIndex(uint x, uint y, T def = T.init)
	{
		if(x >= W  ||  y >= H) return def;
		return _cells[y][x];
	}
	override T opIndexAssign(T rhs, uint x, uint y, T def = T.init)
	{
		if(x >= W  ||  y >= H) return def;
		return _cells[y][x] = rhs;
	}
	override int opApply(int delegate(ref Vector2i, ref T) dg)
	{
		foreach(y, ref row; _cells)
		{
			foreach(x, ref cell; row)
			{
				auto v = Vector2i(x, y);
				const result = dg(v, cell);
				if(result != 0)
					return result;
			}
		}

		return 0;
	}

	override uint width() { return W; }
	override uint height() { return H; }
	override T[] row(uint i) { return _cells[i]; }

	T* ptr() { return _cells.ptr.ptr; }

	private {
		T[W][H]		_cells;
	}
}


class DynamicGrid(T) : Grid!(T) {
	this(uint width, uint height)
	{
		_width = width;
		_height = height;

		_cells.length = height;
		foreach(ref row; _cells)
			row.length = width;
	}
	this(uint width, uint height, T def)
	{
		_width = width;
		_height = height;

		_cells.length = height;
		foreach(ref row; _cells)
			row.length = width;

		fill(def);
	}
	private this()
	{
	}

	override Grid!(T) subGrid(const Vector2i from, const Vector2i to)
	{
		auto r = new SubGrid!(T)(from, to);

		for(int y = from.y;  y < to.y;  ++y)
		{
			r._cells ~= _cells[y][from.x .. to.x];
		}

		return r;
	}

	override void fill(T value)
	{
		foreach(ref row; _cells)
			foreach(ref cell; row)
				cell = value;
	}

	override T* get(uint x, uint y)
	{
		if(x >= width  ||  y >= height) return null;
		return _cells[y].ptr + x;
	}
	override T opIndex(uint x, uint y, T def = T.init)
	{
		if(x >= _width  ||  y >= _height) return def;
		return _cells[y][x];
	}
	override T opIndexAssign(T rhs, uint x, uint y, T def = T.init)
	{
		if(x >= _width  ||  y >= _height) return def;
		return _cells[y][x] = rhs;
	}
	override int opApply(int delegate(ref Vector2i, ref T) dg)
	{
		foreach(y, ref row; _cells)
		{
			foreach(x, ref cell; row)
			{
				auto v = Vector2i(x, y);
				const result = dg(v, cell);
				if(result)
					return result;
			}
		}

		return 0;
	}

	override uint width() { return _width; }
	override uint height() { return _height; }
	override T[] row(uint i) { return _cells[i]; }

	protected {
		T[][]		_cells;
		uint		_width;
		uint		_height;
	}
}


class SubGrid(T) : DynamicGrid!(T) {
	private this(const Vector2i from, const Vector2i to)
	{
		_width = to.x - from.x;
		_height = to.y - from.y;
		_offset = from;
	}

	override int opApply(int delegate(ref Vector2i, ref T) dg)
	{
		foreach(y, ref row; _cells)
		{
			foreach(x, ref cell; row)
			{
				auto v = Vector2i(x, y) + _offset;
				const result = dg(v, cell);
				if(result)
					return result;
			}
		}

		return 0;
	}

	protected {
		Vector2i		_offset;
	}
}
